package com.moonshineproject.plugin.menu
{
	import com.moonshineproject.IDEModel;
	import com.moonshineproject.events.ChangeLineEncodingEvent;
	import com.moonshineproject.events.CloseTabEvent;
	import com.moonshineproject.events.MenuEvent;
	import com.moonshineproject.events.ShortcutEvent;
	import com.moonshineproject.plugin.PluginBase;
	import com.moonshineproject.plugin.menu.vo.CustomMenu;
	import com.moonshineproject.plugin.menu.vo.CustomMenuItem;
	import com.moonshineproject.plugin.menu.vo.MenuItem;
	import com.moonshineproject.plugin.project.ProjectPlugin;
	import com.moonshineproject.plugin.settings.ISettingsProvider;
	import com.moonshineproject.plugin.settings.SettingsPlugin;
	import com.moonshineproject.plugin.settings.vo.ISetting;
	import com.moonshineproject.plugin.settings.vo.MultiOptionSetting;
	import com.moonshineproject.plugin.settings.vo.NameValuePair;
	import com.moonshineproject.utils.KeyboardShortcutManager;
	import com.moonshineproject.vo.KeyboardShortcut;
	import com.moonshineproject.vo.Settings;
	
	import flash.desktop.NativeApplication;
	import flash.display.NativeMenu;
	import flash.display.NativeMenuItem;
	import flash.display.NativeWindow;
	import flash.events.Event;
	import flash.ui.Keyboard;
	import flash.utils.Dictionary;
	
	import mx.core.FlexGlobals;

	// This class is a singleton
	public class MenuPlugin extends PluginBase implements ISettingsProvider
	{

		// If you add menus, make sure to add a constant for the event + a binding for a command in IDEController
		public static const MENU_QUIT_EVENT:String = "menuQuitEvent";
		public static const MENU_OPEN_EVENT:String = "menuOpenEvent";
		public static const MENU_SAVE_EVENT:String = "menuSaveEvent";
		public static const MENU_SAVE_AS_EVENT:String = "menuSaveAsEvent";


		private const BUILD_NATIVE_MENU:uint = 1;
		private const BUILD_CUSTOM_MENU:uint = 2;
		private const BUILD_NATIVE_CUSTOM_MENU:uint = 3;

		// Menu Event to data mapping, used for passing extra information to 
		// listeners
		private var eventToMenuMapping:Dictionary = new Dictionary();

		override public function get name():String { return "Application Menu Plugin"; }
		override public function get author():String { return "Keyston Clay & Moonshine Project Team"; }
		override public function get description():String { return "Adds Menu"; }		

		
		public function getSettingsList():Vector.<ISetting>
		{

			var nvps:Vector.<NameValuePair> = Vector.<NameValuePair>([
				new NameValuePair("Native", BUILD_NATIVE_MENU),
				new NameValuePair("Custom", BUILD_CUSTOM_MENU)
				]);

			if (Settings.os != "win")
			{
				nvps.push(new NameValuePair("Native & Custom", BUILD_NATIVE_CUSTOM_MENU));
			}
			return Vector.<ISetting>([
				new MultiOptionSetting(this, "activeMenus", "Select your menu", nvps)
				]);
		}

		// Data structure for Application window on Mac, Window menu on Windows and to-be-figured-out on Lunix.
		protected var macMenu:MenuItem = new MenuItem("Moonshine");
		protected var quitMenuItem:MenuItem = new MenuItem("Quit", null, MENU_QUIT_EVENT,
															"q", [Keyboard.COMMAND],
															"f4", [Keyboard.CONTROL]);
		protected var settingsMenuItem:MenuItem = new MenuItem("Settings", null, SettingsPlugin.EVENT_OPEN_SETTINGS,
																",", [Keyboard.COMMAND]);										 
		protected var windowMenus:Vector.<MenuItem> = Vector.<MenuItem>([
				new MenuItem("File", [
					new MenuItem("New", []),
					new MenuItem("Open", null, MENU_OPEN_EVENT,
						'o', [Keyboard.COMMAND],
						'o', [Keyboard.CONTROL]),
					new MenuItem(null),
					new MenuItem("Save", null, MENU_SAVE_EVENT,
						's', [Keyboard.COMMAND],
						's', [Keyboard.CONTROL]),
					new MenuItem("Save As...", null, MENU_SAVE_AS_EVENT,
						's', [Keyboard.COMMAND, Keyboard.SHIFT],
						's', [Keyboard.CONTROL, Keyboard.SHIFT]),
					new MenuItem(null),
					new MenuItem("Line Endings", [
						new MenuItem("Windows (CRLF - \\r\\n)", null, ChangeLineEncodingEvent.EVENT_CHANGE_TO_WIN),
						new MenuItem("UNIX (LF - \\n)", null, ChangeLineEncodingEvent.EVENT_CHANGE_TO_UNIX),
						new MenuItem("OS9 (CR - \\r)", null, ChangeLineEncodingEvent.EVENT_CHANGE_TO_OS9)
					]),
					new MenuItem(null),
					new MenuItem("Open resource", null, ProjectPlugin.EVENT_SHOW_OPEN_RESOURCE,
						'r', [Keyboard.COMMAND],
						'r', [Keyboard.CONTROL] 
						),
					new MenuItem(null),
					new MenuItem("Close", null, CloseTabEvent.EVENT_CLOSE_TAB,
						'w', [Keyboard.COMMAND],
						'w', [Keyboard.CONTROL])
				]),
				new MenuItem("View", [])
			]);

		public var activeMenus:uint = Settings.os != "win" ? BUILD_NATIVE_MENU : BUILD_CUSTOM_MENU;

		protected static var shortcutManager:KeyboardShortcutManager = KeyboardShortcutManager.getInstance();

		private var pluginMenuObject:MenuItem = new MenuItem("Plugin", []);
		private var viewMenuObject:MenuItem = new MenuItem("View");
		private var buildingNativeMenu:Boolean = false;
		


		override public function activate():void
		{
			super.activate();
			init();
		}

		override public function deactivate():void
		{
		}



		public function addMenu(menu:MenuItem):void
		{

		}

		public function addPluginMenu(menu:MenuItem):void
		{
			if (!menu)
				return;
			// If we have an assigned parent, loop down & place the menu there.
			if (menu.parents)
			{
				recurseAssignMenu(menu, windowMenus);
			}
			else // Otherwise we toss it in the 'Plugins' menu.
			{
				pluginMenuObject.items.push(menu);
			}
		}

		private function init():void
		{
			// Place stuff in OS-specific menus 
			if (Settings.os == "mac") 
			{
				windowMenus.splice(0, 0, macMenu);
				macMenu.items = new Vector.<MenuItem>();
				macMenu.items.push(settingsMenuItem);
			}
			else
			{
				// End of file menu
				windowMenus[0].items.push(new MenuItem(null));
				windowMenus[0].items.push(settingsMenuItem);
			}
			windowMenus[0].items.push(new MenuItem(null));
			windowMenus[0].items.push(quitMenuItem);
			
			
			if (!activated)
				return;

			// Add Plugin menu before View menu
			if (pluginMenuObject.items.length)
				windowMenus.splice(windowMenus.length-1, 0, pluginMenuObject);		

			if (activeMenus == BUILD_NATIVE_MENU || activeMenus == BUILD_NATIVE_CUSTOM_MENU)
			{
				buildingNativeMenu = true;
				createMenu();
			}

			if (activeMenus == BUILD_CUSTOM_MENU || activeMenus == BUILD_NATIVE_CUSTOM_MENU)
			{
				buildingNativeMenu = false;
				createMenu();
			}
			
			dispatcher.addEventListener(ShortcutEvent.SHORTCUT_PRE_FIRED, handleShortcutPreFired)
		}

		// Adds menu to internal menu representation at a given point. MenuItem.parents decide where it goes.
		protected function recurseAssignMenu(menuItem:MenuItem, children:Vector.<MenuItem>):void
		{
			var target:String = (menuItem.parents.length) ? menuItem.parents[0] : menuItem.label;

			for each (var m:MenuItem in children)
			{
				if (m.label == target)
				{
					if (m.items == null)
						m.items = new Vector.<MenuItem>(1);
					menuItem.parents.splice(0, 1);
					recurseAssignMenu(menuItem, m.items);
					return;
				}
			}

			if (menuItem.parents.length == 0)
			{
				// Target menu found, just add children.
				for each (var submenuItem:MenuItem in menuItem.items)
				{
					children.push(submenuItem);
				}
			}
			else
			{
				// Menu not found, add it.
				children.push(menuItem);
			}
		}

		private function createMenu():void
		{
			var mainMenu:* = buildingNativeMenu ? new NativeMenu() : new CustomMenu();
			addMenus(windowMenus, mainMenu);
			if (buildingNativeMenu)
			{

				var ide:Moonshine = Moonshine(FlexGlobals.topLevelApplication);
				if (NativeApplication.supportsMenu)
					ide.nativeApplication.menu = mainMenu;
				if (NativeWindow.supportsMenu)
					ide.nativeWindow.menu = mainMenu;
			}
			else
			{
				var menuBar:MenuBar = new MenuBar();
				menuBar.menu = mainMenu;
				IDEModel.getInstance().mainView.addChildAt(menuBar, 0);
			}

		}


		protected function createNewMenu():*
		{
			return buildingNativeMenu ? new NativeMenu() : new CustomMenu();
		}

		private function createNewMenuItem(item:MenuItem):*
		{
			var nativeMenuItem:NativeMenuItem
			var menuItem:CustomMenuItem
			var shortcut:KeyboardShortcut = buildShortcut(item);
			if (buildingNativeMenu)
			{
				nativeMenuItem = new NativeMenuItem(item.label, item.isSeparator);

				if (item[Settings.os + "_key"])
					nativeMenuItem.keyEquivalent = item[Settings.os + "_key"];
				if (item[Settings.os + "_mod"])
					nativeMenuItem.keyEquivalentModifiers = item[Settings.os + "_mod"];
				if (item.event)
				{
					// TODO : don't like this
					nativeMenuItem.data = {
							eventData:item.data,
							event:item.event
						};
					eventToMenuMapping[item.event] = nativeMenuItem;
					nativeMenuItem.addEventListener(Event.SELECT, redispatch);
				}
				

			}
			else
			{
				menuItem = new CustomMenuItem(item.label, item.isSeparator);
				if (shortcut)
				{
					menuItem.shortcut = shortcut;
				}
				else if (item.event)
				{
				// TODO : dont like this either :/
					menuItem.data = {
							eventData:item.data,
							event:item.event
						}
					eventToMenuMapping[item.event] = menuItem;
				}

			}
			if (shortcut)
				registerShortcut(shortcut);

			return buildingNativeMenu ? nativeMenuItem : menuItem;

		}

		private function buildShortcut(item:MenuItem):KeyboardShortcut
		{
			var key:String
			var mod:Array
			var event:String

			if (item[Settings.os + "_key"])
				key = item[Settings.os + "_key"];
			if (item[Settings.os + "_mod"])
				mod = item[Settings.os + "_mod"];
			if (item.event)
				event = item.event
			if (event && key)
				return new KeyboardShortcut(event, key, mod);
			return null;
		}

		private function registerShortcut(shortcut:KeyboardShortcut):void
		{
			shortcutManager.activate(shortcut);
		}

		// Loop through menu structure and add menus through handler
		protected function addMenus(items:Vector.<MenuItem>, parentMenu:*):void
		{
			for (var i:int = 0; i < items.length; i++)
			{
				var item:MenuItem = items[i];

				if (item.items)
				{
					var newMenu:*;
					newMenu = createNewMenu();
					if (!newMenu)
						continue;
					addMenus(item.items, newMenu);
					parentMenu.addSubmenu(newMenu, item.label);
				}
				else
				{
					var menuItem:* = createNewMenuItem(item);
					if (menuItem)
						parentMenu.addItem(menuItem);
				}
			}
		}

		// Take events and redispatch them through GED.
		protected function redispatch(event:Event):void
		{
			var menuItem:NativeMenuItem = event.target as NativeMenuItem;

			if (menuItem && menuItem.data)
			{
				var eventType:String = menuItem.data.event as String;

				if (eventType)
				{
					shortcutManager.stopEvent(eventType); // use to stop pending event from shortcut					
				}
			}
		}

		protected function handleShortcutPreFired(e:ShortcutEvent):void
		{
			if (!eventToMenuMapping[e.event])
				return;
			var data:Object = eventToMenuMapping[e.event].data;
			e.preventDefault();
			dispatcher.dispatchEvent(new MenuEvent(
				data.event, false, false,
				data.eventData))

		}

	}
}